
#import <MachOView.h>

/* $Log:	MachOView.m,v $
Revision 1.18  94/05/29  15:00:14  ediger
zero out a memory allocation to make it clear it's unused

Revision 1.17  94/05/28  16:17:55  ediger
finally got section names to draw acceptably.

Revision 1.16  94/05/26  21:21:06  ediger
correct address bracket zooming, hi and lo address bracket checking

Revision 1.15  94/05/23  20:32:13  ediger
lot's o' stuff to do address bracket zoom-in

Revision 1.14  94/05/17  23:09:48  ediger
support for stacking of zoomed address ranges, changed types of
some instance vars from id to pointer to that kind of object.

Revision 1.13  94/05/10  22:41:07  ediger
support for displaying a "zooming box" by mouse drag

Revision 1.12  94/05/04  11:19:58  ediger
rearrange drawSelf:: to be a bit more modular

Revision 1.10  94/05/01  17:32:57  ediger
efficiency improvements

Revision 1.9  94/02/07  21:37:17  ediger
Use of memory-mapped file object to manage file mapping,
add display of segment names and use of matrix of buttons
for choices of what to display.

Revision 1.7  94/01/30  16:27:34  ediger
mostly correct address spacing

Revision 1.6  94/01/01  23:02:22  ediger
broke address label spacing calcs into separate method.

Revision 1.5  93/12/31  17:32:28  ediger
better address label spacing

Revision 1.3  93/12/30  19:01:37  ediger
support for showing upper and lower addresses
of memory mapped segments.

Revision 1.2  93/12/19  14:31:43  ediger
corrected scaling and inclusion of mapped segments of library files

Revision 1.1  93/12/19  12:57:04  ediger
Initial revision

 */

@implementation MachOView

static char rcsident[] = "$Id: MachOView.m,v 1.18 94/05/29 15:00:14 ediger Exp Locker: ediger $";

#undef DEBUG
#ifdef DEBUG
#define D(c) c
#define ENTER(m) fprintf(stderr, "enter %s\n", m);
#define EXIT(m) fprintf(stderr, "exit %s\n", m);
#else
#define D(c)
#define ENTER(m)
#define EXIT(m)
#endif

//M+	-findSizes
//	PURPOSE:
//		Sort through the addresses of the mapped segments in the Mach-O file
//		to be represented, determining the high address, low address, and
//		the number of bytes used in the address space.
//
//	EDITORIAL:
//		Should only be called after using MachOFile instance var to
//		open or re-open the Mach-O file being represented.
//M-
- findSizes
{
	int iSegments, iCC;
	unsigned long ulBaseAddr, ulUpperAddr;
	char bvBuf[64];

	ENTER("- findSizes");

	loAddr = (unsigned long)-1;  /* cheat */
	sumAddr = hiAddr = 0;

	iSegments = [theFile mappedSegments];

	for (iCC = 0; iCC < iSegments; ++iCC)
	{	id oSegment = [theFile mappedSegment:iCC];

		ulBaseAddr = [oSegment getBaseAddress];
		if (loAddr > ulBaseAddr) loAddr = ulBaseAddr;
		ulUpperAddr = [oSegment getUpperAddress];
		if (hiAddr < ulUpperAddr) hiAddr = ulUpperAddr;
		sumAddr += (ulUpperAddr - ulBaseAddr);
	}

	sprintf(bvBuf, "0x%x", loAddr);
	[minAddress setStringValue:bvBuf];
	sprintf(bvBuf, "0x%x", hiAddr);
	[maxAddress setStringValue:bvBuf];

	EXIT("- findSizes");
	return self;
}

//M+	-(char *)fileType
//M-
- (char *)fileType
{
	return [theFile fileType];
}

//M+	-openFileNamed:(char *)fileName
//
//M-
- openFileNamed:(char *)fileName
{
	ENTER("- openFileNamed:");
	assert(NULL != fileName);

	[self freeInternals];

	currentFileName = strcpy(malloc(strlen(fileName) + 1), fileName);

	mappedFile = [[[MappedFile alloc] init] filename:currentFileName];
	if (mappedFile == NULL || [mappedFile map] == FALSE)
	{	[cpuType    setStringValue:"Problem opening"];
		[cpuSubtype setStringValue:currentFileName];

	} else {
		theFile = [[[MachOFile alloc]init]
			fillFromAddress:[mappedFile address]];
		if (NULL == theFile)
		{	[cpuType    setStringValue:"Not a"];
			[cpuSubtype setStringValue:"Mach-O file"];
		} else {
			int iSegments = [theFile mappedSegments];
			addressMap = (struct addressMapping *)malloc(
				iSegments * sizeof(struct addressMapping));
			bzero(addressMap, iSegments * sizeof(struct addressMapping));
			[cpuType    setStringValue:[theFile cpuType]];
			[cpuSubtype setStringValue:[theFile cpuSubtype]];
			[[self findSizes] display];
		}
	}

	EXIT("- openFileNamed:");
    return self;
}

//M+	-toggleScaling:sender
//	Action called by clicking on a button.  Changes the "toScale" instance
//	variable, and calls -display, to reflect the change.
//M-
- toggleScaling:sender
{
	if (toScale)
	{
		toScale = FALSE;
		showSectionNames  = FALSE;
		[showSectionsButton setEnabled:NO];
		[showSectionsButton setIntValue:0];

	} else {
		toScale = TRUE;
		[showSectionsButton setEnabled:YES];
		[showSectionsButton setIntValue:0];
	}

	[self display];

	return self;
}


//M+	-toggleAddresses:sender
//	Action called by clicking on a button.  Changes the "showAddresses"
//	instance variable, and calls -display, to reflect the change.
//M-
- toggleAddresses:sender
{
	if (showAddresses)
		showAddresses  = FALSE;
	else
		showAddresses  = TRUE;

	[self display];

	return self;
}

//M+	-toggleNames:sender
//	Action called by clicking on a button.  Changes the "showNames"
//	instance variable, and calls -display, to reflect the change.
//M-
- toggleNames:sender
{
	if (showNames)
		showNames  = FALSE;
	else
		showNames  = TRUE;

	[self display];

	return self;
}

//M+	-toggleThreads:sender
//	Action called by clicking on a button.  Changes the "showThreads"
//	instance variable, and calls -display, to reflect the change.
//M-
- toggleThreads:sender
{
	if (showThreads)
		showThreads  = FALSE;
	else
		showThreads  = TRUE;

	[self display];

	return self;
}

//M+	-toggleSections:sender
//	Action called by clicking on a button.  Changes the "showSectionNames"
//	instance variable, and calls -display, to reflect the change.
//M-
- toggleSections:sender
{
	int iSegments, iCC;

	if (showSectionNames)
		showSectionNames  = FALSE;
	else
		showSectionNames  = TRUE;

	iSegments = [theFile mappedSegments];

	for (iCC = iSegments - 1; iCC > -1; --iCC)
	{
		int             i;
		SegmentCommand *oSegment  = [theFile mappedSegment:iCC];
		int             iSection  = [oSegment numberOfSections];
		struct section *spSection = [oSegment getSection:0];

		if (iSection > 0)
		{
			if (addressMap[iCC].sectionMap == NULL)
				addressMap[iCC].sectionMap =
					(struct sectionNameMapping *)malloc(
						iSection*sizeof(struct sectionNameMapping));

			assert(addressMap[iCC].sectionMap != NULL);

			for (i = 0; i < iSection; ++i)
			{
				addressMap[iCC].sectionMap[i].sectionName
					= spSection[i].sectname;
				addressMap[iCC].sectionMap[i].sectionAddress
					= spSection[i].addr;
			}

		} else
			addressMap[iCC].sectionMap = NULL;
	}

	[self display];

	return self;
}

//M+	-toggleLibraries:sender
//	PURPOSE:
//	Action called by clicking on a button.  Changes the "showLibraries"
//	instance variable, and calls -display, to reflect the change.
//	EDITORIAL:
//	Somewhat more elaborate than the other -toggle* methods, since the
//	Mach-O file object instance variable gets completely redone.  This
//	may be a case of bad design of the MachOFile object percolating up
//	to here.
//M-
- toggleLibraries:sender;
{
	if (theFile != NULL)
		[theFile free];
	theFile = [[MachOFile alloc] init];

	if (NULL != addressMap)
	{
		int iSegments = [theFile mappedSegments];
		while (--iSegments > -1)
			if (addressMap[iSegments].sectionMap)
				free(addressMap[iSegments].sectionMap);

		free(addressMap);
		addressMap = NULL;
	}

	if (showLibraries)
	{
		showLibraries = FALSE;
		[theFile unconsiderMappedFiles];
	} else {
		showLibraries = TRUE;
		[theFile considerMappedFiles];
	}

	[theFile fillFromAddress:[mappedFile address]];

	addressMap = (struct addressMapping *)malloc(
		[theFile mappedSegments] * sizeof(struct addressMapping));
	bzero(addressMap, [theFile mappedSegments] * sizeof(struct addressMapping));

	// since the MachOFile object has been redone, zoom state is meaningless.
	if (NULL != zoomStack)
	{
		void *vp;

		while ((vp = [zoomStack pop]) != NULL)
			free(vp);
	}
	[unzoomButton setEnabled:NO];
	[zoomButton   setEnabled:NO];

	[[self findSizes] display];

	return self;
}

//M+	-windowWillClose:sender
//	PURPOSE:
//		Ditch any memory-consuming instance vars when the window closes.
//	EDITORIAL:
//		one of the Window delegate methods
//M-
- windowWillClose:sender
{
	ENTER("- windowWillClose:");
	[self freeInternals];
	[zoomStack free];
	EXIT("- windowWillClose:");
	return self;
}

//M+	-rescale
//	PURPOSE:
//		calculate the scaling instance vars
//M-
- rescale
{
	float ht;
	ENTER("- rescale");

	ht = NX_HEIGHT(&bounds) - 25.0;

	scaleSlope      = ht/(float)(hiAddr - loAddr);
	scaleYintercept = 5.0 - scaleSlope*(float)loAddr;

	smashedSlope      = ht/(float)sumAddr;
	smashedYintercept = 0.0;

	EXIT("- rescale");
    return self;
}


//M+	-initFrame:(const NXRect *)frameRect
//	PURPOSE:
//		Set a bunch of instance variables to some sensible values.
//
//	EDITORIAL:
//		Override of a View method
//M-
- initFrame:(const NXRect *)frameRect
{
	ENTER("- initFrame:");
	[super initFrame:frameRect];

	toScale       = TRUE;
	showLibraries = FALSE;
	showAddresses = TRUE;
	showNames     = TRUE;
	showThreads   = TRUE;
	showSectionNames = FALSE;

	currentFileName = NULL;
	addressMap      = NULL;
	mappedFile      = NULL;
	theFile         = NULL;
	zoomStack = [[Queue alloc] init];

	theFont = [Font newFont:"Courier" size:(float)(LABEL_HT - LABEL_LEADING)
		matrix:NX_IDENTITYMATRIX];


	EXIT("- initFrame:");
	return self;
}

//M+	-drawSelf:(NXRect *)rects :(int)rectCount
//
//	EDITORIAL:
//		Override of a View method.
//		This is way too complicated.
//M-
- drawSelf:(NXRect *)rects :(int)rectCount
{
	int iSegments, iCC;
	float xCoord, yCoord, yLast;
	ENTER("- drawSelf::");

	[self rescale];

	// make background white and lines black
	PSsetgray(NX_WHITE);
	NXRectFill(&bounds);
	PSsetgray(NX_BLACK);
	NXFrameRect(&bounds);
	PSsetgray(NX_BLACK);

	PSnewpath();
	PSsetlinewidth(LINE_WDTH);

	[theFont set];

	iSegments = [theFile mappedSegments];

	if (iSegments == 0)
		return self;

	/* stagger the lines representing memory allocations */
	xCoord = NX_WIDTH(&bounds)/2.0 - ((float)iSegments/2.0)*LINE_STAGGER;
	yCoord = VERT_MARGIN;
	yLast  = [[theFile mappedSegment:iSegments - 1] getBaseAddress] - 1.0;

	/* count down so that the "not to scale" bit works */
	for (iCC = iSegments - 1; iCC > -1; --iCC)
	{
		id            oSegment    = [theFile mappedSegment:iCC];
		unsigned long ulBaseAddr  = [oSegment getBaseAddress];
		unsigned long ulUpperAddr = [oSegment getUpperAddress];

		if (ulBaseAddr > hiAddr)
			break;  // don't need to check any higher

		if (
			(ulUpperAddr <= hiAddr && ulBaseAddr >= loAddr) ||
			(ulUpperAddr >= loAddr && ulBaseAddr <  loAddr) ||
			(ulUpperAddr >  hiAddr && ulBaseAddr <  hiAddr)
		)
		{

			addressMap[iCC].baseAddress
				= (loAddr < ulBaseAddr)?ulBaseAddr:loAddr;
			addressMap[iCC].endAddress
				= (hiAddr > ulUpperAddr)?ulUpperAddr:hiAddr;

			if (showNames)
				addressMap[iCC].segmentName  = [oSegment commandName];

			if (toScale)
			{
				addressMap[iCC].yCoordBase = 
					(loAddr < ulBaseAddr)?
					scaleSlope*(float)ulBaseAddr + scaleYintercept
					: scaleSlope*(float)loAddr + scaleYintercept
					;
				addressMap[iCC].yCoordTop  =
					(hiAddr > ulUpperAddr) ?
					scaleSlope*(float)ulUpperAddr + scaleYintercept
					: scaleSlope*(float)hiAddr + scaleYintercept
					;

				PSmoveto(xCoord, addressMap[iCC].yCoordBase);
		
				PSlineto(xCoord, addressMap[iCC].yCoordTop);

			} else {

				if (ulBaseAddr != yLast)
					PSmoveto(xCoord, yCoord + SEGMENT_GAP);
				else
					PSmoveto(xCoord, yCoord);

				if (ulBaseAddr != yLast)
					addressMap[iCC].yCoordBase = yCoord + SEGMENT_GAP;
				else
					addressMap[iCC].yCoordBase = yCoord;

				if (ulUpperAddr <= hiAddr && ulBaseAddr >= loAddr)
					yCoord += smashedSlope * (float)(ulUpperAddr - ulBaseAddr)
						+ smashedYintercept;
				else if (ulUpperAddr >= loAddr && ulBaseAddr <  loAddr)
					yCoord += smashedSlope * (float)(ulUpperAddr - loAddr)
						+ smashedYintercept;
				else if (ulUpperAddr >  hiAddr && ulBaseAddr <  hiAddr)
					yCoord += smashedSlope * (float)(hiAddr - ulBaseAddr)
						+ smashedYintercept;

				PSlineto(xCoord, yCoord);

				addressMap[iCC].yCoordTop = yCoord;

				yLast = ulUpperAddr;
			}

			/* stagger to the right, on the way up */
			xCoord += LINE_STAGGER;

		}
	}

	PSstroke();

	if (yLooping)
	{
		// draw on the box marking the zooming zone
		drawBox(sFirstMouseDown.x, sFirstMouseDown.y,
			sMouseDown.x, sMouseDown.y);
	} else {
		[zoomButton setEnabled:NO];
	}

	/* label the ends of each memory segment with its address */
	if (TRUE == showAddresses)
		[self drawAddresses];

	/* label the middle of each memory segment with its name */
	if (TRUE == showNames)
		[self drawSegmentNames];

	/* draw in little markers for threads program counters */
	if (showThreads)
		[self drawThreads];

	if (showSectionNames)
		[self drawSectionNames];

	EXIT("- drawSelf::");
	return self;
}

//M+	-drawThreads
//M-
- drawThreads
{
	int iCC, iThreadCount = [theFile threads];
	int iSegCount = [theFile mappedSegments];

	PSsetlinewidth(0.50);

	for (iCC = 0; iCC < iThreadCount; ++iCC)
	{
		float xCoord, yCoord;
		int iSegNo = [theFile segmentOfThread:iCC];

		// iSegNo is -1 if there's no segment containing this thread's PC.
		// abort(2) causes just this sort of situation.
		if (iSegNo > -1)
		{
			unsigned long ulPCAddr = [[theFile thread:iCC] pc];

			if (ulPCAddr >= loAddr && ulPCAddr <= hiAddr)
			{

				xCoord = NX_WIDTH(&bounds)/2.0
				- ((float)[theFile mappedSegments]/2.0)*(LINE_STAGGER)
				+ (float)(iSegCount - iSegNo) * LINE_STAGGER;

				yCoord = addressMap[iSegNo].yCoordBase
			+ (addressMap[iSegNo].yCoordTop - addressMap[iSegNo].yCoordBase)
			* ((float)ulPCAddr - addressMap[iSegNo].baseAddress)
			/ (addressMap[iSegNo].endAddress-addressMap[iSegNo].baseAddress);

				drawLeftArrowHead(xCoord, yCoord, 8.0);
			}
		}
	}

	return self;
}

//M+	-spaceLabels
//	Figure out the spacing of the address labels
//	Fills in yCoordBaseLabel and yCoordTopLabel fields such that the
//	addresses don't overlap, and don't end up out of the MachOView.
//	yCoordBaseLabel field ends up as -1.0 if it is the same number
//	as the yCoordTopLabel of the preceding (lower in memory) segment.
//M-
- spaceLabels
{
	float lastY;
	float yLastAddress;
	int   iCC, iSegments = [theFile mappedSegments];

	lastY = addressMap[iSegments - 1].baseAddress - 20.0;
	yLastAddress = addressMap[iSegments - 1].yCoordBase - LABEL_HT;

	for (iCC = iSegments - 1; iCC > -1; --iCC)
	{
		if (addressMap[iCC].baseAddress > hiAddr)
			break;  // no need to go any higher

		if (
(addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
		)
		{

			if (addressMap[iCC].baseAddress != lastY)
			{
				if (addressMap[iCC].yCoordBase < yLastAddress + LABEL_HT)
					addressMap[iCC].yCoordBaseLabel
						= yLastAddress + (LABEL_HT + 2.0);
				else
					addressMap[iCC].yCoordBaseLabel=addressMap[iCC].yCoordBase;

				yLastAddress = addressMap[iCC].yCoordBaseLabel;

			} else {
				/* don't need to show this segment's base address */
				addressMap[iCC].yCoordBaseLabel = -1.0;
			}

			if (addressMap[iCC].yCoordTop < yLastAddress + LABEL_HT)
				addressMap[iCC].yCoordTopLabel = yLastAddress + (LABEL_HT+2.0);
			else
				addressMap[iCC].yCoordTopLabel = addressMap[iCC].yCoordTop;

			yLastAddress = addressMap[iCC].yCoordTopLabel;
			lastY = addressMap[iCC].endAddress;

		}
	}

	if (yLastAddress + LABEL_HT > NX_HEIGHT(&bounds))
	{
		yLastAddress = NX_HEIGHT(&bounds) - (LABEL_HT + 2.0);

		/* try to reshuffle address labels so everything fits */
		for (iCC = 0; iCC < iSegments; ++iCC)
		{

			if (addressMap[iCC].endAddress < loAddr)
				break;  // no need to go any lower

			if (
(addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
			)
			{

				if (addressMap[iCC].yCoordTopLabel > yLastAddress)
				{
					addressMap[iCC].yCoordTopLabel = yLastAddress;
					yLastAddress -= (LABEL_HT + 2.0);
				} else if (0.0 > addressMap[iCC].yCoordTopLabel)
					continue;  // this label doesn't show.  go to next visible.
				else
					break;  /* slipped down into a gap */

				if (addressMap[iCC].yCoordBaseLabel > yLastAddress)
				{
					addressMap[iCC].yCoordBaseLabel = yLastAddress;
					yLastAddress -= (LABEL_HT + 2.0);
				} else if (0.0 > addressMap[iCC].yCoordBaseLabel)
					continue;
				else
					break;
			}
		}
	}

	return self;
}

//M+	-spaceNames
//	Figure out the spacing of the segment name labels
//	Fills in yCoordBaseLabel field such that the
//	names don't overlap, and don't end up out of the MachOView.
//	Segment 
//M-
- spaceNames
{
	int   iCC, iSegments = [theFile mappedSegments];
	float yLastAddress;

	yLastAddress = addressMap[iSegments - 1].yCoordBase - LABEL_HT;

	for (iCC = iSegments - 1; iCC > -1; --iCC)
	{
			if (
(addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
			)
		{
		/* space name across width of view */
		addressMap[iCC].nameXcoord = NX_WIDTH(&bounds) - LABEL_MARGIN
			- [theFont getWidthOf:addressMap[iCC].segmentName];

		/* put name in middle of segment vertically */
		addressMap[iCC].nameYcoord =
			(addressMap[iCC].yCoordBase + addressMap[iCC].yCoordTop)/2.0;

		/* handle overlaps */
		if (addressMap[iCC].nameYcoord < yLastAddress + LABEL_HT)
			addressMap[iCC].nameYcoord = yLastAddress + LABEL_HT;

		yLastAddress = addressMap[iCC].nameYcoord;
		}
	}

	if (yLastAddress + LABEL_HT > NX_HEIGHT(&bounds))
	{
		yLastAddress = NX_HEIGHT(&bounds) - (LABEL_HT + 2.0);

		/* try to reshuffle address labels so everything fits */
		for (iCC = 0; iCC < iSegments; ++iCC)
		{
			if (
(addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
			)
			{
			if (addressMap[iCC].nameYcoord > yLastAddress)
			{
				addressMap[iCC].nameYcoord = yLastAddress;
				yLastAddress -= (LABEL_HT + 2.0);
			} else
				break;  /* slipped down into a gap */
			}
		}
	}

	return self;
}

//M+	-free
//M-
- free
{
	ENTER("- free");
	[self freeInternals];

	[zoomStack free];

	[super free];

	EXIT("- free");
	return self;
}

//M+ - freeInternals
//M-
- freeInternals
{
	ENTER("- freeInternals");
	if (NULL != addressMap)
	{
		int iSegments = [theFile mappedSegments];
		while (--iSegments > -1)
			if (addressMap[iSegments].sectionMap)
				free(addressMap[iSegments].sectionMap);

		free(addressMap);
		addressMap = NULL;
	}

	if (NULL != currentFileName)
	{
		free(currentFileName);
		currentFileName = NULL;
	}

	if (NULL != theFile)
	{
		[theFile free];
		theFile = NULL;
	}

	if (NULL != mappedFile)
	{
		[mappedFile free];
		mappedFile = NULL;
	}

	if (NULL != zoomStack)
	{	// free zoomStack's contents
		void *vp;

		while ((vp = [zoomStack pop]) != NULL)
			free(vp);
	}

	EXIT("- freeInternals");
	return self;
}

//M+	-show:sender
//M-
- show:sender
{
	[theWindow makeKeyAndOrderFront:self];

	return self;
}

//M+	-drawAddresses
//M-
- drawAddresses
{
	float yCoord, xCoord;
	int   iCC, iSegments;

	iSegments = [theFile mappedSegments];

	/* figure out address label spacing */
	[self spaceLabels];

	PSnewpath();
	PSsetlinewidth(0.50);  /* thin lines to each segment */
	[theFont set];

	yCoord = VERT_MARGIN;
	xCoord = NX_WIDTH(&bounds)/2.0 - ((float)iSegments/2.0)*(LINE_STAGGER);

	for (iCC = iSegments - 1; iCC > -1; --iCC)
	{
		char bvBuffer[256];

		if (addressMap[iCC].baseAddress > hiAddr)
			break;  // no need to go any higher

		if (
(addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
		)
		{

			if (!(addressMap[iCC].yCoordBaseLabel < 0.0))
			{	sprintf(bvBuffer, "0x%x",
					(unsigned int)addressMap[iCC].baseAddress);
				drawLabel(LABEL_MARGIN, addressMap[iCC].yCoordBaseLabel,
					bvBuffer, xCoord - (LINE_STAGGER - LABEL_MARGIN),
					addressMap[iCC].yCoordBase);
			}

			sprintf(bvBuffer, "0x%x", (unsigned int)addressMap[iCC].endAddress);
			drawLabel(LABEL_MARGIN, addressMap[iCC].yCoordTopLabel, bvBuffer,
				xCoord - (LINE_STAGGER - LABEL_MARGIN),
				addressMap[iCC].yCoordTop);

			xCoord += LINE_STAGGER;

		}
	}

	PSstroke();

	return self;
}

//M+	-drawSegmentNames
//M-
- drawSegmentNames
{
	float xCoord;
	int iCC, iSegments;

	if (FALSE == showAddresses)
		[self spaceLabels];

	[self spaceNames];

	PSnewpath();
	PSsetlinewidth(0.50);
	[theFont set];

	iSegments = [theFile mappedSegments];

	xCoord = NX_WIDTH(&bounds)/2.0
		- ((float)iSegments/2.0)*(LINE_STAGGER) + 5.0;

	for (iCC = iSegments - 1; iCC > -1; --iCC)
	{
		if (addressMap[iCC].baseAddress > hiAddr)
			break;  // no need to go any higher

		if (
(addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
		)
		{

			xCoord += LINE_STAGGER;
			PSmoveto(
				xCoord,
				(addressMap[iCC].yCoordBase + addressMap[iCC].yCoordTop)/2.0);
			PSlineto(
				addressMap[iCC].nameXcoord - 10.0,
				addressMap[iCC].nameYcoord);
			PSmoveto(addressMap[iCC].nameXcoord, addressMap[iCC].nameYcoord);
			PSshow(addressMap[iCC].segmentName);

		}
	}

	PSstroke();

	return self;
}

//M+	-spaceSectionNames
//M-
- spaceSectionNames
{
	int   iCC, iSegments;
	float yLastAddress;

	iSegments = [theFile mappedSegments];

	yLastAddress = addressMap[iSegments - 1].yCoordBase - LABEL_HT;

	for (iCC = iSegments - 1; iCC > -1; --iCC)
	{
		if (addressMap[iCC].baseAddress > hiAddr)
			break;  // no need to go any higher

		if (
(addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
		)
		{
			SegmentCommand *oSegment = [theFile mappedSegment:iCC];
			int             iSection = [oSegment numberOfSections];

			if (iSection > 0 && addressMap[iCC].sectionMap != NULL)
			{
				int   i;

				for (i = 0; i < iSection; ++i)
				{
					if(
						(addressMap[iCC].sectionMap[i].sectionAddress <= hiAddr)
					&&	(addressMap[iCC].sectionMap[i].sectionAddress >= loAddr)
					)
					{

						if (toScale)
						{
							addressMap[iCC].sectionMap[i].nameYcoord
		= scaleSlope*(float)addressMap[iCC].sectionMap[i].sectionAddress
			+ scaleYintercept;
						} else {
							addressMap[iCC].sectionMap[i].nameYcoord
		= smashedSlope*(float)addressMap[iCC].sectionMap[i].sectionAddress
			+ smashedYintercept;
						}

						if (addressMap[iCC].sectionMap[i].nameYcoord <= yLastAddress+LABEL_HT)
			addressMap[iCC].sectionMap[i].nameYcoord = yLastAddress + LABEL_HT;

						yLastAddress = addressMap[iCC].sectionMap[i].nameYcoord;

					}

				}
			}
		}
	}

	return self;
}

//M+	-drawSectionNames
//M-
- drawSectionNames
{
	float xCoord;
	int iCC, iSegments;

	[self spaceSectionNames];

	PSnewpath();
	PSsetlinewidth(0.50);
	[theFont set];

	iSegments = [theFile mappedSegments];

	xCoord = NX_WIDTH(&bounds)/2.0
		- ((float)iSegments/2.0)*(LINE_STAGGER) + 5.0;

	for (iCC = iSegments - 1; iCC > -1; --iCC)
	{
		if (addressMap[iCC].baseAddress > hiAddr)
			break;  // no need to go any higher

		if (
(addressMap[iCC].endAddress <= hiAddr && addressMap[iCC].baseAddress >= loAddr)
||(addressMap[iCC].endAddress >= loAddr && addressMap[iCC].baseAddress < loAddr)
||(addressMap[iCC].endAddress > hiAddr && addressMap[iCC].baseAddress < hiAddr)
		)
		{
			SegmentCommand *oSegment = [theFile mappedSegment:iCC];
			int             iSection = [oSegment numberOfSections];

			if (iSection > 0 && addressMap[iCC].sectionMap != NULL)
			{
				int             i;
				for (i = 0; i < iSection; ++i)
				{
					if(
						(addressMap[iCC].sectionMap[i].sectionAddress <= hiAddr)
					  &&(addressMap[iCC].sectionMap[i].sectionAddress >= loAddr)
					)
					{
						float yCoord;

						if (toScale)
						{
							yCoord
		= scaleSlope*(float)addressMap[iCC].sectionMap[i].sectionAddress
			+ scaleYintercept;
						} else {
							yCoord
		= smashedSlope*(float)addressMap[iCC].sectionMap[i].sectionAddress
			+ smashedYintercept;
						}
						PSmoveto(xCoord, yCoord);
						PSlineto(xCoord + 10.0,
							addressMap[iCC].sectionMap[i].nameYcoord);
						PSmoveto(xCoord + 14.0,
							addressMap[iCC].sectionMap[i].nameYcoord);
						PSshow(addressMap[iCC].sectionMap[i].sectionName);
					}
				}
			}

			xCoord += LINE_STAGGER;
		}
	}

	PSstroke();

	return self;
}

//M+ - mouseDown:(NXEvent *)spTheEvent
//	PURPOSE:
//		draw a box following the user's mouse drags
//M-
- mouseDown:(NXEvent *)spTheEvent
{
	NXEvent *spNextEvent;
	int      iOldMask, iCheckMask, iDragged = 0;
	char     bvBuf[64];

	[self lockFocus];

	iOldMask = [window eventMask];

	iCheckMask = NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK;

	[window setEventMask:(iOldMask|iCheckMask)];

	sFirstMouseDown = spTheEvent->location;
	[self convertPoint:&sFirstMouseDown fromView:nil];

	yLooping = 1;
	while (yLooping)
	{
		iDragged = 1;
		spNextEvent = [NXApp getNextEvent:iCheckMask];

		if ((yLooping = (spNextEvent->type != NX_MOUSEUP)))
		{
			sMouseDown = spTheEvent->location;
			[self convertPoint:&sMouseDown fromView:nil];
			[self display];
		}
	}

	[self unlockFocus];

	[window setEventMask:iOldMask];

	if (!iDragged)
		return self;

	// For sake of aesthetics, address 0x0 is not exactly at bottom of
	// view.  This lets the user drag an address of less than zero,
	// which ends up as a very high address in an unsigned long.
	// The next bits use the view coords to keep user from screwing up.
	if (sFirstMouseDown.y < VERT_MARGIN)
		sFirstMouseDown.y = VERT_MARGIN;
	if (sMouseDown.y < VERT_MARGIN)
		sMouseDown.y = VERT_MARGIN;

	// set displayed coords for user's inspection
	if (toScale)
	{
		hiAddrZoom =
		(unsigned long)((sFirstMouseDown.y - scaleYintercept)/scaleSlope);
		loAddrZoom =
		(unsigned long)((sMouseDown.y - scaleYintercept)/scaleSlope);

	} else {
		int iCC;
		int iSegments = [theFile mappedSegments];
		int iLoSet = 0, iHiSet = 0;
		// find out where the addresses dragged out are

		for (iCC = iSegments - 1;
			iCC > -1 && (!iLoSet || !iHiSet);
			--iCC)
		{
			if (!iLoSet && addressMap[iCC].yCoordBase <= sMouseDown.y
				&& addressMap[iCC].yCoordTop >= sMouseDown.y)
			{
				loAddrZoom =
	(unsigned long)((sMouseDown.y - addressMap[iCC].yCoordBase)
	/(addressMap[iCC].yCoordTop - addressMap[iCC].yCoordBase)
	*(float)(addressMap[iCC].endAddress - addressMap[iCC].baseAddress))
	+ addressMap[iCC].baseAddress;
				iLoSet = 1;

			}

			if (!iHiSet && addressMap[iCC].yCoordBase <= sFirstMouseDown.y
				&& addressMap[iCC].yCoordTop >= sFirstMouseDown.y)
			{
				hiAddrZoom =
	(unsigned long)((sFirstMouseDown.y - addressMap[iCC].yCoordBase)
	/(addressMap[iCC].yCoordTop - addressMap[iCC].yCoordBase)
	*(float)(addressMap[iCC].endAddress - addressMap[iCC].baseAddress))
	+ addressMap[iCC].baseAddress;
				iHiSet = 1;
			}
		}

		if (!iLoSet)
		{
			// sMouseDown.y not in range of any segment
			if (sMouseDown.y > addressMap[0].yCoordTop)
				loAddrZoom = hiAddr;
			else
				loAddrZoom = loAddr;
		}

		if (!iHiSet)
		{
			// sFirstMouseDown.y not in range of any segment
			if (sFirstMouseDown.y > addressMap[0].yCoordTop)
				hiAddrZoom = hiAddr;
			else
				hiAddrZoom = loAddr;
		}
		
	}

	// user may have dragged from bottom to top.
	if (hiAddrZoom < loAddrZoom)
	{	unsigned long ulTmp = hiAddrZoom; hiAddrZoom = loAddrZoom;
		loAddrZoom = ulTmp; }

	// clamp zoomed values no matter what they calculated out as
	if (hiAddrZoom > hiAddr)
		hiAddrZoom = hiAddr;

	if (loAddrZoom < loAddr)
		loAddrZoom = loAddr;

	sprintf(bvBuf, "0x%x", loAddrZoom);
	[minAddress setStringValue:bvBuf];
	sprintf(bvBuf, "0x%x", hiAddrZoom);
	[maxAddress setStringValue:bvBuf];

	[zoomButton setEnabled:YES];

	return self;
}

//M+ - zoom:sender
//	PURPOSE:
//		Do what needs to be done when user clicks on "zoom" button.
//		Essentially, it pushes the current state (high address displayed,
//		low address displayed, and amount of mapped address taked up)
//		on the zoom stack, recalcs the amount of address taken up
//		inside the new high and low addresses, then redisplays.
//M-
- zoom:sender
{
	struct zoomAddress *spZoom;
	int                 iCC;

	// save current "zoom state".
	spZoom = malloc(sizeof(struct zoomAddress));
	assert(spZoom != NULL);
	spZoom->hiAddr = hiAddr;
	spZoom->loAddr = loAddr;
	spZoom->sumAddr = sumAddr;
	[zoomStack push:(void *)spZoom];

	hiAddr = hiAddrZoom;
	loAddr = loAddrZoom;

	// recalculate the amount of the address used by file's mapped segments
	sumAddr = 0;
	for (iCC = [theFile mappedSegments] - 1; iCC > -1; --iCC)
	{
		id            oSegment = [theFile mappedSegment:iCC];
		unsigned long ulBaseAddr, ulUpperAddr;

		ulBaseAddr  = [oSegment getBaseAddress];
		ulUpperAddr = [oSegment getUpperAddress];

		if (ulBaseAddr > hiAddr)
			break;

		if (
		  (ulUpperAddr <= hiAddr && ulBaseAddr >= loAddr)
		||(ulUpperAddr >= loAddr && ulBaseAddr <  loAddr)
		||(ulUpperAddr >  hiAddr && ulBaseAddr <  hiAddr)
		)
		{
				sumAddr +=
					(ulUpperAddr <= hiAddr ? ulUpperAddr : hiAddr)
					-
					(ulBaseAddr >= loAddr ? ulBaseAddr : loAddr);
		}
	}

	[unzoomButton setEnabled:YES];

	[self display];

	return self;
}

//M+ - unzoom:sender
//	PURPOSE:
//		"unzoom" a level.
//M-
- unzoom:sender
{
	char bvBuf[64];
	struct zoomAddress *spZoom;

	spZoom = (struct zoomAddress *)[zoomStack pop];

	loAddr = spZoom->loAddr;
	hiAddr = spZoom->hiAddr;
	sumAddr = spZoom->sumAddr;
	free(spZoom);

	sprintf(bvBuf, "0x%x", loAddr);
	[minAddress setStringValue:bvBuf];
	sprintf(bvBuf, "0x%x", hiAddr);
	[maxAddress setStringValue:bvBuf];

	if ([zoomStack isEmpty])
		[unzoomButton setEnabled:NO];

	[self display];

	return self;
}

@end
